#include <DAVE.h>                 //Declarations from DAVE Code Generation (includes SFR declaration)
#include "xmc_vadc.h"

volatile static float g_acomp_mV = 0;
volatile static float g_acomp_mV_average = 0;
volatile static float g_temperature = 0;
volatile static float g_temperature_average = 0;
volatile static float g_voltage = 0;
volatile static float g_voltage_average = 0;
volatile static float g_voltage_out_max = 20000;
volatile static float g_current = 0;
volatile static float g_current_average = 0;
volatile static float g_current_max = 0;
volatile static float g_P_out;
volatile static uint32_t g_ADC_voltage, g_ADC_current, g_ADC_temperature, g_ADC_acomp, g_ADC_vref;
volatile static float g_ADC_vref_average=880;
volatile uint8_t g_clamp = 1, led_r, led_b, led_up, led_down;
volatile static uint32_t counter_20ms = 0;

volatile static uint32_t g_overvoltage_detected=0;
volatile static uint32_t g_overcurrent_detected=0;
volatile static uint32_t g_overtemperature_detected=0;
volatile static uint32_t g_critical_detected=0;

#define VOLTAGE_MAX					28000
#define VOLTAGE_HYSTERESIS			1000
#define CURRENT_MAX					11000
#define CURRENT_HYSTERESIS			1000
#define TEMP_MAX					110
#define TEMP_HYSTERESIS				20
#define ACOMP_MAX           		2000
#define ACOMP_HYSTERESIS    		50
#define VREF_VALUE_MV       		2480

volatile static uint8_t dataBuffer[12];
volatile static uint8_t data = 0;
volatile static uint8_t stateASK = 0, parity = 0, i = 0;
volatile static uint32_t voltage;
volatile static uint32_t current;
volatile static uint8_t counter_receive = 0;
volatile static uint8_t counter_wait = 0;

void check_threshold (uint32_t value, uint32_t threshold, uint32_t hysteresis_offset, volatile uint32_t* status)
{
	if ( ( value > threshold + hysteresis_offset) && ( *status == 0 ) )
	{
		*status = 1;
	}
	if ( ( value < threshold - hysteresis_offset) && ( *status == 1 ) )
	{
		*status = 0;
	}
	return;
}

void data_init(void){ //Initializes the data which wants to be transfered (dataBuffer[0] gets its value in the main function)
	voltage = g_voltage;
	current = g_current;
	dataBuffer[1] = 0b11110000;
	dataBuffer[2] = (uint8_t)((voltage >> 24) & 0xFF);
	dataBuffer[3] = (uint8_t)((voltage >> 16) & 0xFF);
	dataBuffer[4] = (uint8_t)((voltage >> 8) & 0xFF);
	dataBuffer[5] = (uint8_t)(voltage & 0xFF);
	dataBuffer[6] = 0b00111100;
	dataBuffer[7] = (uint8_t)((current >> 24) & 0xFF);
	dataBuffer[8] = (uint8_t)((current >> 16) & 0xFF);
	dataBuffer[9] = (uint8_t)((current >> 8) & 0xFF);
	dataBuffer[10] = (uint8_t)(current & 0xFF);
	dataBuffer[11] = 0b10101010;
}

void data_transfer_init(void){ //Calls data_init and activates the interrupt for data transmission
	data_init();
	INTERRUPT_Enable(&INTERRUPT_ASK);
	return;
}

int main(void)
{
  DAVE_STATUS_t status;

  status = DAVE_Init();           /* Initialization of DAVE APPs  */

  if(status != DAVE_STATUS_SUCCESS)
  {
    /* Placeholder for error handler code. The while loop below can be replaced with an user error handler. */
    XMC_DEBUG("DAVE APPs initialization failed\n");

    while(1U)
    {

    }
  }
  /* Placeholder for user application code. The while loop below can be replaced with user application code. */
  while(1U)
  {
		/* Check for overvoltage, -current, temperature situations */
		check_threshold (g_voltage_average, VOLTAGE_MAX, VOLTAGE_HYSTERESIS, &g_overvoltage_detected);
		check_threshold (g_current_average, CURRENT_MAX, CURRENT_HYSTERESIS, &g_overcurrent_detected);
		check_threshold (g_temperature_average, TEMP_MAX, TEMP_HYSTERESIS, &g_overtemperature_detected);

		g_critical_detected = g_overvoltage_detected | g_overcurrent_detected | g_overtemperature_detected | g_clamp;

		if ( g_critical_detected )
		{
//			DIGITAL_IO_SetOutputHigh(&CLAMP);
			DIGITAL_IO_SetOutputHigh(&G_LED); /*Micrium:*/ led_b=1;
			DIGITAL_IO_SetOutputLow(&R_LED);  /*Micrium:*/ led_r=0;
		}
		else
		{
			DIGITAL_IO_SetOutputLow(&CLAMP);
			DIGITAL_IO_SetOutputLow(&G_LED);  /*Micrium:*/ led_b=0;
			DIGITAL_IO_SetOutputHigh(&R_LED); /*Micrium:*/ led_r=1;
		}

		if (counter_20ms >= 5)
		{
			counter_20ms = 0;
			if (g_voltage < g_voltage_out_max)
			{
				/* Power Up */
				DIGITAL_IO_SetOutputLow(&UP_LED); led_up = 0;
				DIGITAL_IO_SetOutputHigh(&DOWN_LED); led_down = 1;
				dataBuffer[0] = 0b00000000;
			}
			else
			{
				/* Power Down */
				DIGITAL_IO_SetOutputHigh(&UP_LED); led_up = 1;
				DIGITAL_IO_SetOutputLow(&DOWN_LED); led_down = 0;
				dataBuffer[0] = 0b11111111;
			}
		}
  }
}

float average_calc(float new_value, float current_average, uint16_t filter)
{
	return (current_average*((float)filter-1) + new_value)/filter;
}

void task_20ms(void)
{
	const int16_t ads_max_plus1 = 1024;
	const float Ntc_teiler = 3.74;
	const float Ntc_offset = 121;
	const float R1_  = 1000;
	const float R2_  = 43;
	const float Ref_ = 5000;
	float T1, T2;
	volatile static uint16_t ADC_voltage, ADC_current, ADC_temperature;
	float temperature_s;

	counter_20ms++;

	/* Read analog to digital converters */
	g_ADC_voltage     = ADC_MEASUREMENT_ADV_GetResult(&VOLT_CURR_TEMP_Voltage_handle) / 4; // 4-samples filter inside ADC_MEASUREMENT_ADV enabled
	g_ADC_current     = ADC_MEASUREMENT_ADV_GetResult(&VOLT_CURR_TEMP_Current_handle) / 4; // 4-samples filter inside ADC_MEASUREMENT_ADV enabled
	g_ADC_temperature = ADC_MEASUREMENT_ADV_GetResult(&VOLT_CURR_TEMP_Temperature_handle) / 4; // 4-samples filter inside ADC_MEASUREMENT_ADV enabled

	/* Temperature processing
	 *               (Adc_max_plus1 - Adc_wert)
	 *  Temperatur = -------------------------- - Ntc_Offset
	 *                    Ntc_Teiler
	 */
	ADC_temperature = g_ADC_temperature / 4;	// 12 bit --> 10bit
	temperature_s = ads_max_plus1 - (float)ADC_temperature;
	temperature_s = temperature_s / Ntc_teiler;
	temperature_s = temperature_s - Ntc_offset;

	// TODO: Remove fixed temperature
	g_temperature = 40;
	if (g_temperature_average == 0)
		g_temperature_average = g_temperature;
	g_temperature_average = average_calc(g_temperature, g_temperature_average, 16);


	/* Voltage processing
	 *          (R1 + R2) * Ref * Adc_Wert
	 *   U_out = --------------------------
	 *           R2 * ADC_max_plus1
	 */
	ADC_voltage = g_ADC_voltage / 4;	// 12 bit --> 10bit

	T1 = (R1_ + R2_) * Ref_;
	T2 = R2_ * ads_max_plus1;
	g_voltage = ((float)ADC_voltage * T1) / T2;

	if (g_voltage_average == 0)
		g_voltage_average = g_voltage;
	g_voltage_average = average_calc(g_voltage, g_voltage_average, 16);

	/* Current processing
	 *
	 */
	ADC_current = g_ADC_current / 4;	// 12 bit --> 10bit
	g_current =  4.38*ADC_current * 10000 / 826;
	if (g_current_average == 0)
		g_current_average = g_current;
	g_current_average = average_calc(g_current, g_current_average, 16);

	/* Power processing
	 */
	g_P_out = g_voltage * g_current * 1E-6;

	return;
}

void ACOMP_ISR(void)
{
	static uint8_t startup_blank = 0;

	/* For the first 160ms (32*5ms) after startup ignore clamp */
	if (startup_blank < 32)
		startup_blank++;

	/* Read analog to digital converters */
	g_ADC_acomp = ADC_MEASUREMENT_ADV_GetResult(&ACOMP_ACOMP_handle)/4; // 4-samples filter inside ADC_MEASUREMENT_ADV enabled
	g_ADC_vref  =ADC_MEASUREMENT_ADV_GetResult(&ACOMP_VRef_handle)/4; // 4-samples filter inside ADC_MEASUREMENT_ADV enabled
	g_ADC_vref_average = average_calc((float)g_ADC_vref, g_ADC_vref_average, 256);

	g_acomp_mV = (float)g_ADC_acomp * VREF_VALUE_MV / g_ADC_vref_average;;
	g_acomp_mV_average = average_calc(g_acomp_mV, g_acomp_mV_average, 32);

	if (((g_acomp_mV_average > ACOMP_MAX + ACOMP_HYSTERESIS ) && (g_clamp == 0) && (startup_blank>=32)))
	{
		// Upper boundary transition
		g_clamp = 1;
	}
	if ((g_acomp_mV_average < ACOMP_MAX - ACOMP_HYSTERESIS ) && (g_clamp == 1))
	{
		// Lower boundary transition
		g_clamp = 0;
	}
	return;
}

void ASK_ISR(void)
{
		data = dataBuffer[counter_receive];
		switch((uint8_t)stateASK){

		case (uint8_t)0: //Pause
					stateASK++;
					break;

		case (uint8_t)1: //Pause
					stateASK++;
					break;

		case (uint8_t)2:
					DIGITAL_IO_ToggleOutput(&ASK_OUT); //Start bit: Always toggle
					stateASK++;
					break;

		case (uint8_t)3: //Start bit 2nd half. Doesn't toggle, because start bit = 0
					stateASK++;
					break;

		case (uint8_t)4: //Bit 0 1st half
					DIGITAL_IO_ToggleOutput(&ASK_OUT);  //Always toggle new bit
					stateASK++;
					break;

		case (uint8_t)5: //Bit 0 2nd half
					if ((data & (0b1 << (stateASK-5)))>=0b1){
						DIGITAL_IO_ToggleOutput(&ASK_OUT); //Toggle if bit = 1
					}
					stateASK++;
					break;

		case (uint8_t)6: //Bit 1
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					stateASK++;
					break;

		case (uint8_t)7: if ((data & (0b1 << ((stateASK-5)/2)))>=0b1){
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					}
					stateASK++;
					break;

		case (uint8_t)8: //Bit 2
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					stateASK++;
					break;

		case (uint8_t)9: if ((data & (0b1 << ((stateASK-5)/2)))>=0b1){
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					}
					stateASK++;
					break;

		case (uint8_t)10: //Bit 3
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					stateASK++;
					break;

		case (uint8_t)11: if ((data & (0b1 << ((stateASK-5)/2)))>=0b1){
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					}
					stateASK++;
					break;

		case (uint8_t)12: //Bit 4
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					stateASK++;
					break;

		case (uint8_t)13: if ((data & (0b1 << ((stateASK-5)/2)))>=0b1){
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					}
					stateASK++;
					break;

		case (uint8_t)14: //Bit 5
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					stateASK++;
					break;

		case (uint8_t)15: if ((data & (0b1 << ((stateASK-5)/2)))>=0b1){
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					}
					stateASK++;
					break;

		case (uint8_t)16: //Bit 6
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					stateASK++;
					break;

		case (uint8_t)17: if ((data & (0b1 << ((stateASK-5)/2)))>=0b1){
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					}
					stateASK++;
					break;

		case (uint8_t)18: //Bit 7
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					stateASK++;
					break;

		case (uint8_t)19: if ((data & (0b1 << ((stateASK-5)/2)))>=0b1){
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					}
					stateASK++;
					break;

		case (uint8_t)20: //Parity bit
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					stateASK++;
					break;

		case (uint8_t)21: for(i=0;i<8;i++){ //Calculate parity
						if((data & (0b1 << (i)))>=0b1)
							parity++;
						}
					parity=parity%2;
					if (parity >= 0b1){
					DIGITAL_IO_ToggleOutput(&ASK_OUT); //Toggle if parity = 1
					}
					stateASK++;
					break;

		case (uint8_t)22: //Stop bit
					DIGITAL_IO_ToggleOutput(&ASK_OUT);
					stateASK++;
					break;

		case (uint8_t)23: //Stop bit
					DIGITAL_IO_ToggleOutput(&ASK_OUT); //Always toggle, because stop bit = 1
					parity=0;
					counter_receive++;
					stateASK=0;
					if(counter_receive > 11){
					INTERRUPT_Disable(&INTERRUPT_ASK);
					counter_receive = 0;
					}
					break;

		}
return;
}
